package com.yb0os1.builder;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class SQL {
    private SQL() {
    }

    public static FromStage select(String... columns) {
        return new SQLSelectBuilder(columns);
    }

    public static SQLUpdateBuilder update() {
        return new SQLUpdateBuilder();
    }

    public static AddStage insert(String table) {
        return new SQLInsertBuilder(table);
    }

    public static class SQLInsertBuilder extends AbstractBuilder implements AddStage {
        private String table;
        private String addExpression;

        public SQLInsertBuilder(String table) {
            this.table = table;
        }

        //insert into user () values()
        @Override
        public AbstractBuilder add(String addExpression) {
            this.addExpression = addExpression;
            return this;
        }

        @Override
        public String build() {
            StringBuilder sb = new StringBuilder();
            return sb.append("insert into ")
                    .append(table)
                    .append(" ")
                    .append(addExpression)
                    .toString();
        }


    }

    public static class SQLSelectBuilder extends AbstractBuilder implements FromStage, WhereStage {
        private String table;
        private String where;
        private final String[] columns;

        public SQLSelectBuilder(String... columns) {
            this.columns = columns;
        }

        public WhereStage from(String table) {
            this.table = table;
            return this;
        }

        public SQLSelectBuilder where(String where) {
            this.where = where;
            return this;
        }


        public String build() {
            StringBuilder sql = new StringBuilder();
            sql.append("select ");
            if (columns != null && columns.length > 0) {
                sql.append(String.join(",", columns));
            } else {
                sql.append("*");
            }
            sql.append(" from ").append(table);
            if (where != null) {
                sql.append(" where ").append(where);
            }
            return sql.toString();
        }


    }

    public static class SQLUpdateBuilder extends AbstractBuilder implements TableStage, WhereStage, SetStage {
        private String table;
        private String where;
        private String set;

        private SQLUpdateBuilder() {
        }

        public SetStage table(String table) {
            this.table = table;
            return this;
        }

        public AbstractBuilder where(String where) {
            this.where = where;
            return this;
        }

        public WhereStage set(String set) {
            this.set = set;
            return this;
        }

        @Override
        public String build() {
            //update xxx set xxx=xxx,xxx=xxx where xxx=xxx
            StringBuilder sql = new StringBuilder();
            return sql.append("update ")
                    .append(table).append(" set ")
                    .append(set)
                    .append(where)
                    .toString();
        }
    }

    interface TableStage {
        SetStage table(String table);
    }

    interface WhereStage {
        AbstractBuilder where(String where);
    }

    interface SetStage {
        WhereStage set(String set);
    }

    interface FromStage {
        WhereStage from(String table);
    }

    interface AddStage {
        AbstractBuilder add(String add);
    }

    static abstract class AbstractBuilder {
        public abstract String build();
    }


    public static SetBuilder setBulider() {
        return new SetBuilder();
    }

    public static class SetBuilder extends AbstractBuilder {
        private Map<String, String> map = new LinkedHashMap<>();

        private SetBuilder() {
        }

        public SetBuilder set(String col, String val) {
            map.put(col, val);
            return this;
        }

        public String build() {
            return map.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.joining(","));
        }
    }

    public static WhereBuilderWhereStage whereBulider() {
        return new WhereBuilder();
    }

    public static class WhereBuilder extends AbstractBuilder implements WhereBuilderWhereStage, WhereBuilderAndOrStage {
        private Map<String, String> map = new LinkedHashMap<>();

        private WhereBuilder() {
        }

        public WhereBuilderAndOrStage where(String where) {
            map.put(where, "");
            return this;
        }

        public WhereBuilderAndOrStage and(String where) {
            map.put(where, "and");
            return this;
        }

        public WhereBuilderAndOrStage or(String where) {
            map.put(where, "or");
            return this;
        }

        @Override
        public String build() {
            StringBuilder sb = new StringBuilder();
            sb.append(" where ")
                    .append(map.entrySet().stream()
                            .map(entry -> {
                                        if (!entry.getValue().isEmpty()) {
                                            return entry.getValue() + " " + entry.getKey();
                                        }
                                        return entry.getKey();
                                    }
                            ).collect(Collectors.joining(" ")));
            return sb.toString();
        }
    }

    interface WhereBuilderWhereStage {
        WhereBuilderAndOrStage where(String where);
    }

    interface WhereBuilderAndOrStage {
        WhereBuilderAndOrStage and(String where);

        WhereBuilderAndOrStage or(String where);

        String build();
    }

    public static AddBuilder insertBuilder() {
        return new AddBuilder();
    }

    public static class AddBuilder extends AbstractBuilder {
        private Map<String, String> map = new LinkedHashMap<>();

        private AddBuilder() {
        }

        public AddBuilder add(String col, String val) {
            map.put(col, val);
            return this;
        }

        @Override
        public String build() {
            StringBuilder key = new StringBuilder();
            StringBuilder value = new StringBuilder();
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String, String> entry :entries) {
                key.append(entry.getKey());
                value.append(entry.getValue());
                //如果entry不是最后一个
                if (entry != entries.toArray()[map.size() - 1]) {
                    key.append(" , ");
                    value.append(" , ");
                }
            }
            return "(" + key + ") values (" + value + ")";
        }


    }
}

